#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
数据中心核心模块
负责独立运行的数据中心服务，提供全市场行情订阅、数据持久化、K线合成等功能
"""

import json
import threading
import time
import traceback
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional, Set, Any

from src.config.constant import Exchange
from src.config.setting import get_instrument_exchange_id
from src.core.event import Event, EventType
from src.core.event_bus import EventBus
from src.core.logger import get_logger
from src.core.object import TickData, BarData, ContractData

from src.services.data_center_bar_generator import DataCenterBarGenerator
from src.services.data_center_database import DataCenterDatabase
from src.ctp.gateway.market_data_gateway import MarketDataGateway


logger = get_logger("DataCenter")


class DataCenter:
    """
    数据中心核心类
    负责全市场行情订阅、数据持久化、K线合成、历史查询等功能
    """
    
    def __init__(self, event_bus: EventBus, config: dict[str, Any]):
        """
        初始化数据中心
        
        Args:
            config: 配置字典
            event_bus: 事件总线
        """
        self.config = config
        self.event_bus = event_bus
        
        # 运行状态
        self.is_running = False
        self.is_connected = False
        
        # 数据库管理器
        self.database = DataCenterDatabase(config)
        
        # K线合成器
        intervals = config.get('bar_intervals', [1, 5, 15, 30, 60])
        self.bar_generator = DataCenterBarGenerator(
            intervals=intervals,
            on_bar=self._on_bar_generated
        )
        
        # 网关
        self.gateway: Optional[MarketDataGateway] = None
        
        # 订阅管理
        self.subscribed_symbols: Set[str] = set()
        self.all_contracts: Dict[str, ContractData] = {}
        
        # 全市场合约列表
        self.market_symbols = self._load_market_symbols()
        
        # 统计信息
        self.stats = {
            'tick_count': 0,
            'bar_count': 0,
            'start_time': datetime.now(),
            'last_tick_time': datetime.now(),
            'subscribed_count': 0,
            'database_status': 'disconnected'
        }
        
        # 监控线程
        self.monitor_thread: Optional[threading.Thread] = None
        self.health_check_thread: Optional[threading.Thread] = None
        
        # 注册事件处理器
        self._register_event_handlers()
        
        logger.info("数据中心初始化完成")
    
    def _load_market_symbols(self) -> List[str]:
        """加载全市场合约列表"""
        try:
            # 从配置文件加载合约列表
            symbols_file = self.config.get("symbols_file", "config/market_symbols.json")
            symbols_path = Path(symbols_file)
            
            if symbols_path.exists():
                with open(symbols_path, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    return data.get("symbols", [])
            else:
                logger.warning(f"合约配置文件不存在: {symbols_path}，使用默认合约列表")
                return []
                
        except Exception as e:
            logger.error(f"加载市场合约列表失败: {e}")
            return []
    
    def _register_event_handlers(self):
        """注册事件处理器"""
        # 数据中心查询事件
        self.event_bus.subscribe(EventType.DATA_CENTER_QUERY_TICK, self._handle_query_tick)
        self.event_bus.subscribe(EventType.DATA_CENTER_QUERY_BAR, self._handle_query_bar)
        
        # 网关事件
        self.event_bus.subscribe(EventType.GATEWAY_CONNECTED, self._handle_gateway_connected)
        self.event_bus.subscribe(EventType.GATEWAY_DISCONNECTED, self._handle_gateway_disconnected)
        self.event_bus.subscribe(EventType.CONTRACT_INFO, self._handle_contract_info)
        self.event_bus.subscribe(EventType.DATA_CENTER_TICK, self._handle_tick_data)
        
        logger.info("数据中心事件处理器注册完成")
    
    def start(self):
        """启动数据中心"""
        try:
            if self.is_running:
                logger.warning("数据中心已在运行")
                return
            
            logger.info("启动数据中心...")
            
            # 启动数据库
            self.database.start()
            
            # 初始化网关
            self._init_gateway()
            
            # 启动监控线程
            self._start_monitor_threads()
            
            # 设置运行状态
            self.is_running = True
            self.stats['start_time'] = datetime.now()
            self.stats['database_status'] = 'connected'
            
            # 发布数据中心连接事件
            self.event_bus.publish(Event(EventType.DATA_CENTER_CONNECTED, {}))
            
            logger.info("数据中心启动成功")
            
        except Exception as e:
            logger.error(f"启动数据中心失败: {e}", exc_info=True)
            self.stop()
            raise
    
    def stop(self):
        """停止数据中心"""
        try:
            if not self.is_running:
                logger.warning("数据中心未在运行")
                return
            
            logger.info("停止数据中心...")
            
            # 设置停止标志
            self.is_running = False
            
            # 停止网关
            if self.gateway:
                self.gateway.close()
                self.gateway = None
            
            # 停止数据库
            self.database.stop()
            
            # 停止监控线程
            self._stop_monitor_threads()
            
            # 发布数据中心断开事件
            self.event_bus.publish(Event(EventType.DATA_CENTER_DISCONNECTED, {}))
            
            logger.info("数据中心已停止")
            
        except Exception as e:
            logger.error(f"停止数据中心失败: {e}", exc_info=True)
    
    def _init_gateway(self):
        """初始化网关"""
        try:
            # 从配置获取网关设置
            gateway_config = self.config.get('gateway', {})
            
            # 创建行情网关实例
            self.gateway = MarketDataGateway(
                event_bus=self.event_bus,
                gateway_name="DATA_CENTER_MD"
            )
            
            # 连接网关
            self.gateway.connect(gateway_config)
            
            logger.info("网关初始化成功")
            
        except Exception as e:
            logger.error(f"网关初始化失败: {e}")
            raise
    
    def _start_monitor_threads(self):
        """启动监控线程"""
        try:
            # 启动性能监控线程
            self.monitor_thread = threading.Thread(
                target=self._performance_monitor,
                daemon=True
            )
            self.monitor_thread.start()
            
            logger.info("监控线程启动成功")
            
        except Exception as e:
            logger.error(f"启动监控线程失败: {e}")
            raise
    
    def _stop_monitor_threads(self):
        """停止监控线程"""
        try:
            # 等待监控线程结束
            if hasattr(self, 'monitor_thread') and self.monitor_thread.is_alive():
                self.monitor_thread.join(timeout=5)
            
            logger.info("监控线程已停止")
            
        except Exception as e:
            logger.error(f"停止监控线程失败: {e}")
    
    def _performance_monitor(self):
        """性能监控线程"""
        logger.info("性能监控线程已启动")
        
        while self.is_running:
            try:
                time.sleep(30)  # 每30秒输出一次统计信息
                
                # 计算运行时间
                if self.stats['start_time']:
                    uptime = datetime.now() - self.stats['start_time']
                    uptime_str = str(uptime).split('.')[0]  # 去掉微秒
                else:
                    uptime_str = "未知"
                
                # 输出统计信息
                logger.info(
                    f"📊 数据中心运行状态 - "
                    f"运行时间: {uptime_str}, "
                    f"Tick数量: {self.stats['tick_count']}, "
                    f"K线数量: {self.stats['bar_count']}, "
                    f"订阅合约: {self.stats['subscribed_count']}, "
                    f"数据库状态: {self.stats['database_status']}"
                )
                
            except Exception as e:
                logger.error(f"性能监控异常: {e}")
                time.sleep(5)
        
        logger.info("性能监控线程已停止")
    
    def _handle_tick_data(self, event: Event):
        """处理tick数据"""
        try:
            tick_data = event.data
            
            # 如果是TickData对象，直接使用
            if isinstance(tick_data, TickData):
                processed_tick = tick_data
            # 如果是字典，转换为TickData对象
            elif isinstance(tick_data, dict):
                from src.core.object import TickData as TickDataClass
                from src.config.constant import Exchange
                from datetime import datetime
                
                # 转换字典为TickData对象
                processed_tick = TickDataClass(
                    symbol=tick_data.get('symbol', ''),
                    exchange=Exchange(tick_data.get('exchange', 'CZCE')),
                    datetime=datetime.fromisoformat(tick_data.get('datetime', datetime.now().isoformat())),
                    last_price=tick_data.get('last_price', 0.0),
                    volume=tick_data.get('volume', 0),
                    turnover=tick_data.get('turnover', 0.0),
                    open_interest=tick_data.get('open_interest', 0),
                    bid_price_1=tick_data.get('bid_price_1', 0.0),
                    ask_price_1=tick_data.get('ask_price_1', 0.0),
                    bid_volume_1=tick_data.get('bid_volume_1', 0),
                    ask_volume_1=tick_data.get('ask_volume_1', 0),
                    gateway_name="DATA_CENTER_MD"
                )
            else:
                logger.warning(f"收到未知类型的tick数据: {type(tick_data)}")
                return
            
            # 更新统计信息
            self.stats['tick_count'] += 1
            self.stats['last_tick_time'] = datetime.now()
            
            # 保存到数据库
            self.database.save_tick_data(processed_tick)
            
            # K线合成
            self._process_bar_generation(processed_tick)
            
            # 定期输出统计信息
            if self.stats["tick_count"] % 100 == 0:
                logger.info(f"📊 数据中心统计: 已处理{self.stats['tick_count']}个tick, 当前合约={processed_tick.symbol}")
            
        except Exception as e:
            logger.error(f"处理tick数据失败: {e}", exc_info=True)
    
    def _process_bar_generation(self, tick_data: TickData):
        """处理K线合成"""
        try:
            # 使用K线合成器生成K线
            self.bar_generator.on_tick(tick_data)
                        
        except Exception as e:
            logger.error(f"K线合成处理失败: {e}", exc_info=True)
    
    def _on_bar_generated(self, bar: BarData):
        """K线生成回调"""
        try:
            # 更新统计信息
            self.stats['bar_count'] += 1
            
            # 保存到数据库
            self.database.save_bar_data(bar)
            
            logger.debug(f"生成K线: {bar.symbol} {bar.datetime}")
            
        except Exception as e:
            logger.error(f"处理K线数据失败: {e}", exc_info=True)
    
    def _handle_gateway_connected(self, event: Event):
        """处理网关连接事件"""
        try:
            self.is_connected = True
            self.stats['gateway_status'] = 'connected'
            
            # 订阅全市场行情
            self._subscribe_market_data()
            
            logger.info("网关已连接，开始订阅全市场行情")
            
        except Exception as e:
            logger.error(f"处理网关连接事件失败: {e}")
    
    def _handle_gateway_disconnected(self, event: Event):
        """处理网关断开事件"""
        try:
            self.is_connected = False
            self.stats['gateway_status'] = 'disconnected'
            
            logger.warning("网关已断开连接")
            
        except Exception as e:
            logger.error(f"处理网关断开事件失败: {e}")
    
    def _handle_contract_info(self, event: Event):
        """处理合约信息事件"""
        try:
            contract = event.data
            symbol = contract.symbol
            
            # 添加到合约列表
            self.all_contracts[symbol] = contract
            
            logger.info(f"新增合约: {symbol}")
            
        except Exception as e:
            logger.error(f"处理合约信息失败: {e}")
    
    def _subscribe_market_data(self):
        """订阅全市场行情数据"""
        try:
            if not self.gateway:
                logger.warning("网关未初始化，无法订阅行情")
                return
            
            # 预加载合约数据到全局缓存
            self._preload_contracts()
            
            # 优先订阅配置文件中的合约
            symbols_to_subscribe = self.market_symbols.copy()
            
            # 如果有通过CONTRACT_INFO事件获取的合约，也加入订阅列表
            for symbol in self.all_contracts:
                if symbol not in symbols_to_subscribe:
                    symbols_to_subscribe.append(symbol)
            
            # 订阅所有合约的tick数据
            for symbol in symbols_to_subscribe:
                # 创建订阅请求对象
                from src.core.object import SubscribeRequest
                from src.config.constant import Exchange
                
                # 根据合约代码推断交易所（简单映射）
                exchange = self._get_symbol_exchange(symbol)
                
                subscribe_req = SubscribeRequest(
                    symbol=symbol,
                    exchange=exchange
                )
                
                self.gateway.subscribe(subscribe_req)
                self.subscribed_symbols.add(symbol)
                logger.info(f"订阅合约: {symbol} (交易所: {exchange.value})")
            
            self.stats['subscribed_count'] = len(self.subscribed_symbols)
            logger.info(f"已订阅 {len(self.subscribed_symbols)} 个合约的行情数据")
            
        except Exception as e:
            logger.error(f"订阅全市场行情失败: {e}")

    @staticmethod
    def _get_symbol_exchange(symbol: str):
        """根据合约代码推断交易所"""
        exchange = get_instrument_exchange_id().get(symbol, "")
        return Exchange(exchange)
    
    def _preload_contracts(self):
        """预加载合约数据到全局缓存"""
        try:
            # 导入全局合约映射
            from src.ctp.gateway.market_data_gateway import symbol_contract_map
            from src.core.object import ContractData
            from src.config.constant import Product
            
            logger.info("开始预加载合约数据...")
            
            # 获取交易所映射
            instrument_exchange_json = get_instrument_exchange_id()
            
            # 为所有要订阅的合约创建合约数据
            symbols_to_load = self.market_symbols.copy()
            
            # 如果有通过CONTRACT_INFO事件获取的合约，也加入加载列表
            for symbol in self.all_contracts:
                if symbol not in symbols_to_load:
                    symbols_to_load.append(symbol)
            
            for symbol in symbols_to_load:
                if symbol not in symbol_contract_map:
                    exchange_str = instrument_exchange_json.get(symbol, "CZCE")
                    
                    # 将字符串转换为Exchange枚举
                    if exchange_str == "CZCE":
                        exchange = Exchange.CZCE
                    elif exchange_str == "SHFE":
                        exchange = Exchange.SHFE
                    elif exchange_str == "DCE":
                        exchange = Exchange.DCE
                    elif exchange_str == "CFFEX":
                        exchange = Exchange.CFFEX
                    else:
                        exchange = Exchange.CZCE
                    
                    # 创建合约数据
                    contract = ContractData(
                        symbol=symbol,
                        exchange=exchange,
                        name=f"{symbol}合约",
                        product=Product.FUTURES,
                        size=1,
                        price_tick=0.01,
                        min_volume=1,
                        gateway_name=self.gateway.gateway_name if self.gateway else "DATA_CENTER_MD"
                    )
                    
                    # 添加到全局缓存
                    symbol_contract_map[symbol] = contract
                    logger.info(f"预加载合约: {symbol} -> {exchange.value}")
            
            logger.info(f"合约预加载完成，共加载 {len(symbols_to_load)} 个合约，全局缓存数量: {len(symbol_contract_map)}")
            
        except Exception as e:
            logger.error(f"预加载合约数据失败: {e}")
            logger.error(f"错误详情: {traceback.format_exc()}")
    
    def _handle_query_tick(self, event: Event):
        """处理tick数据查询请求"""
        try:
            query_data = event.data
            symbol = query_data.get('symbol')
            start_time = query_data.get('start_time')
            end_time = query_data.get('end_time')
            limit = query_data.get('limit', 1000)
            
            # 查询数据
            tick_data = self.database.query_tick_data(
                symbol=symbol,
                start_time=start_time,
                end_time=end_time,
                limit=limit
            )
            
            # 发布查询结果
            result_event = Event(
                type=EventType.DATA_CENTER_TICK_RESULT,
                data={
                    'request_id': query_data.get('request_id'),
                    'tick_data': tick_data
                }
            )
            self.event_bus.publish(result_event)
            
        except Exception as e:
            logger.error(f"处理tick查询请求失败: {e}")
    
    def _handle_query_bar(self, event: Event):
        """处理bar数据查询请求"""
        try:
            query_data = event.data
            symbol = query_data.get('symbol')
            exchange = query_data.get('exchange', 'SHFE')  # 默认上期所
            interval = query_data.get('interval')
            start_time = query_data.get('start_time')
            end_time = query_data.get('end_time')
            limit = query_data.get('limit', 1000)
            
            # 查询数据
            bar_data = self.database.query_bar_data(
                symbol=symbol,
                exchange=exchange,
                interval=interval,
                start_time=start_time,
                end_time=end_time,
                limit=limit
            )
            
            # 发布查询结果
            result_event = Event(
                type=EventType.DATA_CENTER_BAR_RESULT,
                data={
                    'request_id': query_data.get('request_id'),
                    'bar_data': bar_data
                }
            )
            self.event_bus.publish(result_event)
            
        except Exception as e:
            logger.error(f"处理bar查询请求失败: {e}")
    
    def get_status(self) -> dict:
        """获取数据中心状态"""
        try:
            status = {
                'is_running': self.is_running,
                'is_connected': self.is_connected,
                'stats': self.stats.copy(),
                'subscribed_symbols': list(self.subscribed_symbols),
                'total_contracts': len(self.all_contracts),
                'database_status': self.database.get_status() if self.database else 'disconnected'
            }
            
            return status
            
        except Exception as e:
            logger.error(f"获取状态失败: {e}")
            return {'error': str(e)}
    
    def query_tick_data(self, symbol: str, start_time: datetime = None, 
                       end_time: datetime = None, limit: int = 1000) -> List[dict]:
        """查询tick数据"""
        try:
            return self.database.query_tick_data(
                symbol=symbol,
                start_time=start_time,
                end_time=end_time,
                limit=limit
            )
        except Exception as e:
            logger.error(f"查询tick数据失败: {e}")
            return []
    
    def query_bar_data(self, symbol: str, exchange: str = 'SHFE', interval: str = '1m', 
                      start_time: datetime = None, end_time: datetime = None, limit: int = 1000) -> List[dict]:
        """查询bar数据"""
        try:
            return self.database.query_bar_data(
                symbol=symbol,
                exchange=exchange,
                interval=interval,
                start_time=start_time,
                end_time=end_time,
                limit=limit
            )
        except Exception as e:
            logger.error(f"查询bar数据失败: {e}")
            return []


if __name__ == "__main__":
    # 独立运行示例
    import time
    from src.core.event_bus import EventBus
    
    def main():
        # 创建事件总线
        event_bus = EventBus()
        
        # 配置
        config = {
            "sqlite_path": "data/market_data.db",
            "parquet_path": "data/parquet",
            "tick_batch_size": 100,
            "bar_batch_size": 50,
            "tick_flush_interval": 5,
            "bar_flush_interval": 10,
            "parquet_compression": "snappy",
            "symbols_file": "config/market_symbols.json",
            "gateway": {
                "type": "ctp",
                "broker_id": "9999",
                "user_id": "your_user_id",
                "password": "your_password",
                "auth_code": "your_auth_code",
                "app_id": "your_app_id"
            }
        }
        
        # 创建数据中心
        data_center = DataCenter(event_bus=event_bus, config=config)
        
        try:
            # 启动数据中心
            data_center.start()
            
            # 保持运行
            logger.info("数据中心已启动，按Ctrl+C停止")
            while data_center.is_running:
                time.sleep(1)
                
        except KeyboardInterrupt:
            logger.info("收到停止信号")
        finally:
            data_center.stop()
            logger.info("数据中心已停止")
    
    # 运行
    main()